package com.meizi.codegenerate.generate.server.application.service.assembler;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.IfStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.meizi.codegenerate.constant.AnnotationSettingValueConstant;
import com.meizi.codegenerate.constant.JavaClassNameConstant;
import com.meizi.codegenerate.constant.MethodNameConstant;
import com.meizi.codegenerate.constant.TableNameConstant;
import com.meizi.codegenerate.entity.ProjectPositionEntity;
import com.meizi.codegenerate.entity.TablesEntity;
import com.meizi.codegenerate.generate.server.application.service.desensitizer.DesensitizerGenerate;
import com.meizi.codegenerate.packagegenerate.PackageGenerate;
import com.meizi.codegenerate.utils.AnnotationUtils;
import com.meizi.codegenerate.utils.AttributeConvertUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * @author pc
 * @date 2024/1/31 - 11:34 - 星期三
 * @project lambda
 * @package com.meizi.codegenerate.generate.server.application.service.assembler
 * @by GGbomd猪猪侠
 */
@Slf4j
public class AssemblerGenerate {


    /**
     * 生成 汇编器
     * @param respModelName
     * @param respBodyClassName
     */
    public static void createAssemblerGenerate(String tableName, String respModelName, String respBodyClassName,
                                               TablesEntity tablesComment, ProjectPositionEntity projectPosition,
                                               String assemblerClassName, String assemblerValueName) throws IOException {

        // 在相应的路径下创建对应的文件
        String serviceUrl = PackageGenerate.obtainApplicationServiceAssemblerUrl(projectPosition);

        // 获取完成的文件路径创建文件
        String fullPath = serviceUrl + assemblerClassName + TableNameConstant.JAVA;
        log.info("创建 application 下 Assembler 路径 fullPath = {}", fullPath);

        CompilationUnit compilationUnit = new CompilationUnit();

        // 添加package
        compilationUnit.setPackageDeclaration(PackageGenerate.applicationServiceAssemblerPackage(projectPosition));

        compilationUnit.addImport("lombok.extern.slf4j.Slf4j");
        compilationUnit.addImport("org.springframework.stereotype.Component");
        compilationUnit.addImport("javax.annotation.Resource");

        // 添加接口文件
        ClassOrInterfaceDeclaration declaration = compilationUnit.addClass(assemblerClassName).setPublic(true);
        //添加类级别注释
        declaration.setJavadocComment(tablesComment.getTableComment() + " 汇编器");
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();

        // 添加类级别注解
        declaration.addAnnotation(Slf4j.class);

        map.put(AnnotationSettingValueConstant.SERVICE_VALUE, assemblerValueName);
        declaration.addAnnotation(AnnotationUtils.setAnnotation(Component.class, new NodeList<>(),
                map));
        map.clear();

        // 注入相关实现类
        String desensitizerClassName = AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, true)
                + JavaClassNameConstant.DESENSITIZER;
        String desensitizerValueName = AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, false)
                + JavaClassNameConstant.DESENSITIZER;

        map.put(AnnotationSettingValueConstant.RESOURCE_NAME, desensitizerValueName);
        map.put(AnnotationSettingValueConstant.SERVICE_TYPE, desensitizerClassName.getBytes());
        declaration.addField(desensitizerClassName, desensitizerValueName, Modifier.Keyword.PRIVATE)
                .addAnnotation(AnnotationUtils.setAnnotation(Resource.class, new NodeList<>(), map));
        map.clear();

        //将 领域Model 转成 RespBody
        MethodDeclaration modelToRespBody = methodModelToRespBody(MethodNameConstant.SERVICE_CONVERT_MODEL_TO_RESP_BODY, respBodyClassName, respModelName);
        declaration.addMember(modelToRespBody);

        MethodDeclaration modelToBody = methodModelToBody(MethodNameConstant.SERVICE_CONVERT_MODEL_TO_RESP_BODY, respBodyClassName, respModelName,desensitizerValueName);
        declaration.addMember(modelToBody);

        //将 领域Model列表 转成 RespBody列表
        MethodDeclaration modelToRespBodyList = methodModelToRespBodyList(MethodNameConstant.SERVICE_CONVERT_MODEL_LIST_TO_RESP_BODY_LIST, respBodyClassName, respModelName);
        declaration.addMember(modelToRespBodyList);

        MethodDeclaration modelToBodyList = methodModelToBodyList(MethodNameConstant.SERVICE_CONVERT_MODEL_LIST_TO_RESP_BODY_LIST, respBodyClassName, respModelName,desensitizerValueName);
        declaration.addMember(modelToBodyList);


        log.info("生成Assembler接口 compilationUnit = {}", compilationUnit.toString());

        /**
         * 把字符串写入文件
         * 参数1：需要写入的文件，如果文件不存在，将自动创建。  参数2：需要写入的内容
         * 参数3：编码格式     参数4：是否为追加模式（ ture: 追加模式，把字符串追加到原内容后面）
         */
        FileUtils.writeStringToFile(new File(fullPath), compilationUnit.toString(), "UTF-8", false);

        // 生成脱敏器
        DesensitizerGenerate.createDesensitizer(respModelName, tablesComment, projectPosition, desensitizerClassName, desensitizerValueName);
    }

    private static MethodDeclaration methodModelToBodyList(String modelListToRespBodyList, String respBodyClassName,
                                                           String respModelName, String desensitizerValueName) {
        String list = "list";
        String desensitization = "desensitization";
        String respBody = "respBody";
        String illustrate = "将 领域Model列表 转成 RespBody列表\n" +
                "\t  \n" +
                "\t  @param list List<%s> 领域Model\n" +
                "\t  @param desensitization Boolean 是否脱敏\n" +
                "\t  @return List<%s>";

        // 添加返回值类型
        ClassOrInterfaceType resp = new ClassOrInterfaceType(respBodyClassName);
        ClassOrInterfaceType respList = new ClassOrInterfaceType(null, List.class.getSimpleName());
        respList.setTypeArguments(resp);

        ClassOrInterfaceType model = new ClassOrInterfaceType(respModelName);
        ClassOrInterfaceType modelList = new ClassOrInterfaceType(null, List.class.getSimpleName());
        modelList.setTypeArguments(model);

        // 生成方法
        MethodDeclaration methodDeclaration = new MethodDeclaration();
        methodDeclaration.setModifiers(Modifier.Keyword.PUBLIC);
        methodDeclaration.setType(respList);
        methodDeclaration.setName(modelListToRespBodyList);
        methodDeclaration.setJavadocComment(String.format(illustrate, respModelName, respBodyClassName));
        methodDeclaration.addParameter(new Parameter(modelList, list));
        methodDeclaration.addParameter(new Parameter(new ClassOrInterfaceType(null, Boolean.class.getSimpleName()), desensitization));


        IfStmt repeatIfStmtNULL = new IfStmt();
        repeatIfStmtNULL.setCondition(new NameExpr(desensitization));
        BlockStmt blockStmt = new BlockStmt(
                NodeList.nodeList(
                        new ExpressionStmt(
                                new MethodCallExpr(
                                        new NameExpr(desensitizerValueName),
                                        MethodNameConstant.SERVICE_DESENSITIZER,
                                        NodeList.nodeList(new NameExpr(list), new NameExpr(desensitization))
                                )
                        )
                )
        );
        repeatIfStmtNULL.setThenStmt(blockStmt);

        //  转换成model
        ClassOrInterfaceType respBodyType = new ClassOrInterfaceType(null, respBodyClassName);
        MethodCallExpr modelMethodCallExpr = new MethodCallExpr(
                new NameExpr("BeanConvertUtils"),
                "convertListTo"
        );
        modelMethodCallExpr.addArgument(new NameExpr(list));
        modelMethodCallExpr.addArgument(new NameExpr(respBodyClassName + "::new"));
        VariableDeclarationExpr modelValuesVar = new VariableDeclarationExpr(
                new VariableDeclarator(
                        respList,
                        respBody,
                        modelMethodCallExpr
                )
        );

        // 生成返回
        ReturnStmt returnStmt = new ReturnStmt(respBody);

        methodDeclaration.getBody().ifPresent(body -> body.addStatement(repeatIfStmtNULL));
        methodDeclaration.getBody().ifPresent(body -> body.addStatement(modelValuesVar));
        methodDeclaration.getBody().ifPresent(body -> body.addStatement(returnStmt));
        return methodDeclaration;
    }

    private static MethodDeclaration methodModelToRespBodyList(String modelToRespBody, String respBodyClassName, String respModelName) {

        String list = "list";
        String illustrate = "将 领域Model列表 转成 RespBody列表\n" +
                "\t  \n" +
                "\t  @param list List<%s> 领域Model\n" +
                "\t  @return List<%s>";

        // 添加返回值类型
        ClassOrInterfaceType resp = new ClassOrInterfaceType(respBodyClassName);
        ClassOrInterfaceType respList = new ClassOrInterfaceType(null, List.class.getSimpleName());
        respList.setTypeArguments(resp);

        ClassOrInterfaceType model = new ClassOrInterfaceType(respModelName);
        ClassOrInterfaceType modelList = new ClassOrInterfaceType(null, List.class.getSimpleName());
        modelList.setTypeArguments(model);

        // 生成方法
        MethodDeclaration methodDeclaration = new MethodDeclaration();
        methodDeclaration.setModifiers(Modifier.Keyword.PUBLIC);
        methodDeclaration.setType(respList);
        methodDeclaration.setName(modelToRespBody);
        methodDeclaration.setJavadocComment(String.format(illustrate, respModelName, respBodyClassName));
        methodDeclaration.addParameter(new Parameter(modelList, list));

        MethodCallExpr resultMethodCallExpr = new MethodCallExpr(
                modelToRespBody
        ).addArgument(list).addArgument("true");
        // 生成返回
        ReturnStmt returnStmt = new ReturnStmt(resultMethodCallExpr);

        methodDeclaration.getBody().ifPresent(body -> body.addStatement(returnStmt));

        return methodDeclaration;
    }

    private static MethodDeclaration methodModelToBody(String modelToRespBody, String respBodyClassName,
                                                       String respModelName, String desensitizerValueName) {

        String model = "model";
        String desensitization = "desensitization";
        String respBody = "respBody";
        String illustrate = "将 领域Model 转成 RespBody\n" +
                "\t \n" +
                "\t @param model %s 领域Model\n" +
                "\t @param desensitization Boolean 是否脱敏\n" +
                "\t @return %s";

        // 添加返回值类型
        ClassOrInterfaceType resp = new ClassOrInterfaceType(respBodyClassName);

        // 生成方法
        MethodDeclaration methodDeclaration = new MethodDeclaration();
        methodDeclaration.setModifiers(Modifier.Keyword.PUBLIC);
        methodDeclaration.setType(resp);
        methodDeclaration.setName(modelToRespBody);
        methodDeclaration.setJavadocComment(String.format(illustrate, respModelName, respBodyClassName));
        methodDeclaration.addParameter(new Parameter(new ClassOrInterfaceType(null, respModelName), model));
        methodDeclaration.addParameter(new Parameter(new ClassOrInterfaceType(null, Boolean.class.getSimpleName()), desensitization));


        IfStmt repeatIfStmtNULL = new IfStmt();
        repeatIfStmtNULL.setCondition(new NameExpr(desensitization));
        BlockStmt blockStmt = new BlockStmt(
                NodeList.nodeList(
                        new ExpressionStmt(
                                new MethodCallExpr(
                                        new NameExpr(desensitizerValueName),
                                        MethodNameConstant.SERVICE_DESENSITIZER,
                                        NodeList.nodeList(new NameExpr(model), new NameExpr(desensitization))
                                )
                        )
                )
        );
        repeatIfStmtNULL.setThenStmt(blockStmt);

        //  转换成model
        ClassOrInterfaceType respBodyType = new ClassOrInterfaceType(null, respBodyClassName);
        MethodCallExpr modelMethodCallExpr = new MethodCallExpr(
                new NameExpr("BeanConvertUtils"),
                "convertTo"
        );
        modelMethodCallExpr.addArgument(new NameExpr(model));
        modelMethodCallExpr.addArgument(new NameExpr(respBodyClassName + "::new"));
        VariableDeclarationExpr modelValuesVar = new VariableDeclarationExpr(
                new VariableDeclarator(
                        respBodyType,
                        respBody,
                        modelMethodCallExpr
                )
        );

        // 生成返回
        ReturnStmt returnStmt = new ReturnStmt(respBody);

        methodDeclaration.getBody().ifPresent(body -> body.addStatement(repeatIfStmtNULL));
        methodDeclaration.getBody().ifPresent(body -> body.addStatement(modelValuesVar));
        methodDeclaration.getBody().ifPresent(body -> body.addStatement(returnStmt));
        return methodDeclaration;
    }

    private static MethodDeclaration methodModelToRespBody(String modelToRespBody, String respBodyClassName, String respModelName) {

        String model = "model";
        String illustrate = "将 领域Model 转成 RespBody\n" +
                "\t \n" +
                "\t @param model %s 领域Model\n" +
                "\t @return %s";

        // 添加返回值类型
        ClassOrInterfaceType resp = new ClassOrInterfaceType(respBodyClassName);

        // 生成方法
        MethodDeclaration methodDeclaration = new MethodDeclaration();
        methodDeclaration.setModifiers(Modifier.Keyword.PUBLIC);
        methodDeclaration.setType(resp);
        methodDeclaration.setName(modelToRespBody);
        methodDeclaration.setJavadocComment(String.format(illustrate, respModelName, respBodyClassName));
        methodDeclaration.addParameter(new Parameter(new ClassOrInterfaceType(null, respModelName), model));

        MethodCallExpr resultMethodCallExpr = new MethodCallExpr(
                modelToRespBody
        ).addArgument(model).addArgument("true");
        // 生成返回
        ReturnStmt returnStmt = new ReturnStmt(resultMethodCallExpr);

        methodDeclaration.getBody().ifPresent(body -> body.addStatement(returnStmt));

        return methodDeclaration;
    }
}
